package com.kbtd.utils;

import com.kbtd.dto.UserDTO;

/**
 * 用户上下文信息（使用ThreadLocal）
 * ThreadLocal是什么？（java提供的一个线程本地变量，变量的值在线程上下文中共用，是线程安全的）
 * ThreadLocal用途？（1保存线程上下文信息，在任意需要的地方可以获取(线程数据共享)2线程间数据隔离）
 * ThreadLocal底层？
 * ThreadLocal本身并不存储数据，而是通过ThreadLocalMap操作存储数据，Map的key为ThreadLocal本身
 * ThreadLocal源码分析：
 * 1.threadLocalHashCode成员变量：使用nextHashcode方法生成的魔数hash（散列算法，散列度强），用于在map中作hash值，
 *                              该魔数与int长度比例为黄金分割比例
 * 2.set(): 获取当前线程实例拿到ThreadLocalMap对象（为空则创建），并添加值，key为ThreadLocal实例，value为值
 * 3.get(): 如果线程存在ThreadLocalMap对象且map中存有该实例对应值，直接返回
 *          否则初始化ThreadLocal及Map中该实例对应值（重写initialValue（默认返回null））
 * 4.remove(): 调用ThreadLocalMap.remove方法
 * ThreadLocalMap源码分析
 * 开放寻址法（ThreadLocalMap哈希方法）：发生hash冲突时会向前寻址，直到找到空位插入，比较适合散列程度高的hash(如前面使用的hash魔数)
 * expungeStaleEntry():过期值清理方法，清理过期值并对后值进行重hash(目的维护开放寻址结构)
 * cleanSomeSlots:尝试性扫描清理部分过期数据（内部实际调用expungeStaleEntry）
 * 开放寻址查找过程:计算hash,比较hash值对应数据，冲突向下查找直到查找数据为null（有空位说明没数据）或匹配值停止
 * getEntry():获取map对应threadLocal的值，并且在寻址查找时清理过期数据
 * set():通过hash值向下寻找合适位置 1.该ThreadLocal实例对应key(直接设置修改值)
 *                              2.过期数据位（替换成该实例对象(replaceStaleEntry)，清理部分过期数据）
 *                              3.空位null(直接插入)
 * remove(): 清除entry中的value(唯一强引用置为null)，配合其弱引用性质可被gc回收
 * 弱引用：当一个对象只存在弱引用时，根据可达性算法，该对象是不可达的，在下一次gc过程会被回收
 * 内存泄露：定义静态ThreadLocal对象初始话值，并且线程长期活跃，未调用remove导致内存泄露
 * 最佳实践：及时调用remove方法清理无用数据
 *
 * @author wp
 */
public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}
